package cn.fufeiii.bt.common.util;

import com.fasterxml.jackson.databind.json.JsonMapper;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * http-client-4
 *
 * @author FuFei
 */
public final class HttpClient4Util {
    private static final CloseableHttpClient HTTP_CLIENT;
    private static final JsonMapper JSON_MAPPER;

    static {
        int defaultTimeout = (int) TimeUnit.SECONDS.toMillis(30);
        int defaultKeepAliveDuration = (int) TimeUnit.SECONDS.toMillis(3);

        // 配置相关超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(defaultTimeout).setConnectTimeout(defaultTimeout).setConnectionRequestTimeout(defaultTimeout)
                .build();

        // 若需要重用链接，且未获取到请求中的长链接配置时，使用默认存活时间
        ConnectionKeepAliveStrategy connectionKeepAliveStrategy = (response, context) -> {
            long keepAliveDuration = DefaultConnectionKeepAliveStrategy.INSTANCE.getKeepAliveDuration(response, context);
            return keepAliveDuration > 0 ? keepAliveDuration : defaultKeepAliveDuration;
        };

        HTTP_CLIENT = HttpClientBuilder.create()
                .setMaxConnPerRoute(2)
                .setMaxConnTotal(20)
                .setDefaultRequestConfig(requestConfig)
                .setKeepAliveStrategy(connectionKeepAliveStrategy)
                .build();

        JSON_MAPPER = JsonUtil.newMapper();
    }


    public static <T> T get(String uri, Class<T> clazz) {
        HttpUriRequest request = buildRequest(HttpGet.METHOD_NAME, uri, null, null);
        return submit(request, clazz);
    }

    public static <T> T post(String uri, String requestBody, Class<T> clazz) {
        HttpUriRequest request = buildRequest(HttpPost.METHOD_NAME, uri, requestBody, null);
        return submit(request, clazz);
    }


    private static HttpUriRequest buildRequest(String method, String uri, String requestBody, Map<String, String> headerMap) {
        HttpRequestBase request;
        switch (method) {
            case HttpGet.METHOD_NAME:
                request = new HttpGet();
                break;
            case HttpPost.METHOD_NAME:
                request = new HttpPost();
                break;
            default:
                throw new IllegalArgumentException("不支持的请求类型" + method);
        }
        request.setURI(URI.create(uri));
        if (headerMap != null) {
            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                request.setHeader(entry.getKey(), entry.getValue());
            }
        }
        if (requestBody != null && request instanceof HttpEntityEnclosingRequest) {
            ((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
        }
        return request;
    }

    @SuppressWarnings("unchecked")
    private static <T> T readResponse(HttpEntity httpEntity, Class<T> clazz) throws IOException {
        if (httpEntity == null) {
            return null;
        }
        byte[] responseByteArray = EntityUtils.toByteArray(httpEntity);
        if (String.class.getTypeName().equals(clazz.getTypeName())) {
            return (T) new String(responseByteArray);
        }
        return JSON_MAPPER.readValue(responseByteArray, clazz);
    }

    private static <T> T submit(HttpUriRequest request, Class<T> clazz) {
        try (CloseableHttpResponse closeableHttpResponse = HTTP_CLIENT.execute(request)) {
            HttpEntity entity = closeableHttpResponse.getEntity();
            return readResponse(entity, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
